home *** CD-ROM | disk | FTP | other *** search
/ Almathera Ten Pack 3: CDPD 3 / Almathera Ten on Ten - Disc 3: CDPD3.iso / fish / 001-100 / 001-025 / 001 / requesters / menu.doc < prev    next >
Text File  |  1995-03-17  |  25KB  |  546 lines

  1.  
  2.  
  3.  
  4.                       MENUS, REQUESTORS, AND GADGETS
  5.                                    BY
  6.                              John T. Draper
  7.                   A service from the Programmers Network
  8.                         on the WELL in Sausalito.
  9.  
  10.    Permission to post this on other networks is granted provided the
  11.  source of this information is included.   The programmers network is
  12.  a non-profit network exchange of programming information.   For more
  13.  information,  mail your requests to:
  14.  
  15.  WELL: crunch
  16.  BIX:  crunch
  17.  USENET: ihnp4!ptsfa!well!crunch
  18.  DELPHI: crunch                                 
  19.  
  20.  
  21.         ========================= MENUS =========================
  22.  
  23.     Menus are activated by pressing the RIGHT mouse button, causing 
  24.  little windows to open up along the top of the screen.   When the right
  25.  mouse button is pressed,  a row of words are displayed at the top.  Each
  26.  of these words are the name of the menu. The row of menu names along
  27.  the top are called a MENU STRIP.   
  28.  
  29.     When you point to a name, press the mouse button;  a little window
  30.  or rectangular square is displayed showing the names of the menu ITEMS.
  31.  Notice that, as a menu name is selected,  the name is inverted in a dark
  32.  rectangle.   The width of the rectangle ( in the menu strip),  is
  33.  the MENU NAME WIDTH.   Just below the menu strip is a box.   Contained
  34.  within the box are names for the menu ITEMS.   The width of the box I 
  35.  will call ITEM BOX WIDTH.   The menu name width and the item box width
  36.  have to be chosen properly in order for the menus to work and they are
  37.  related in a specific way.   If they are set up improperly,  they 
  38.  won't work.
  39.  
  40.     It is possible to write procedures that automatically set up the
  41.  proper widths,  but that is beyond the scope of this article.   Such
  42.  a procedure might take up an  unnecessary amount of memory.
  43.  
  44.     Amiga programs use two types of memory: that allocated by initialized
  45.  and declared variables and structures; and that allocated by the memory
  46.  manager supplied by intuition.   This program can easily be modified to
  47.  allocate menus outside of the program memory area, thus making more room
  48.  for large programs.
  49.  
  50.     This example attempts to use the "best of both worlds"  by using a
  51.  short piece of code to initialize and link the Menu items, and yet
  52.  having the flexability offered by initializing the structures at compile
  53.  time.
  54.  
  55.     There are advantages and dis-advantages to each method.   I will attempt
  56.  to explain them here.
  57.  
  58.  METHOD #1 - Initializing the structures at declaration.   For example:
  59.  
  60.  struct Menu req_menu = {
  61.     NL,                             /* NO Next menu           */
  62.     FIL_WIDTH + ITEM_WIDTH,         /* LeftEdge               */
  63.     0, REQ_WIDTH, 10,               /* TopEdge, Width, Height */
  64.     MENUENABLED,                    /* Flags                  */
  65.     "Requestors",                   /* Menu name              */
  66.     req_items                       /* Pointer to items list  */
  67.  };
  68.  
  69.     This code initializes and declares the structure at compile time.
  70.  This is nice for quick programs,  or programs that are small.   BUT!!-
  71.  here's the clincher!!   If your program has a lot if structures to
  72.  initialize,   IT INCREASES COMPILE TIMES. It also clutters up the program, 
  73.  because you have to read through pages and pages of these structure 
  74.  declarations to change your menus.   You cannot easily add or  remove 
  75.  items without going to several places in your code and changing it.   
  76.  It also takes up more program space.   Perhaps it doesn't matter on the 
  77.  Amiga,  but on the Macintosh,  there is a limited size your code module 
  78.  can have.   In the example program that comes with this article,  I use 
  79.  method #1 for ONLY the menus and NOT the menu items, because there is 
  80.  only a limited number of menus,  due to the size of the top strip.  
  81.  (menu bars for those Mac freaks).
  82.  
  83.  METHOD #2 - Calling a function to initialize an array of structures.
  84.  This is what I do in this example.   In this example,   I am only
  85.  using text items- but only a little bit of modification will allow
  86.  for IMAGE items.   The sprite editor,  soon to be here,  contains these
  87.  types of menus.   Usually,  only ONE intuitext and MenuItem structure
  88.  is initialized and are then copied into an array of them.   This array
  89.  can be allocated using the memory manager, then released when finished.
  90.  
  91.  
  92.  
  93.  DATA STRUCTURES USED IN MENUS
  94.  -----------------------------
  95.    
  96.    If you plan on having menus in your program,  you have to prepare two
  97.  basic types of structures,  and initialze their values.   These structures
  98.  have to be initialized properly in order for your program to work.  In
  99.  my first experience of using menus,  I spent over a week figuring them out.
  100.  The intuition manual wasn't too clear on how to set them up.   There
  101.  was an omission of an important field on page 6-15 of the intuition
  102.  manual.   The field is "SelectFill", which is between ItemFill and
  103.  Command.   You should mark this in your manual so you won't be confused.
  104.  
  105.  
  106.  MENU STRUCTURE                                 
  107.  --------------
  108.  
  109.     A "Menu" structure describes the characteristics of the "thingey" that's
  110.  at the TOP of the screen.   It describes LeftEdge,  TopEdge,  Width,
  111.  and Height.   This is NOT, I repeat NOT, the width and height of the
  112.  little window that pops out when that menu is selected.   For instance,
  113.  TopEdge and Height are IGNORED by Intuition.   Leftedge describes the
  114.  leftmost position RELATIVE TO THE SCREEN, where the black
  115.  select box will be rendered.   Eight pixels to the RIGHT of this box is
  116.  where the first character of the NAME of the menu will be rendered.
  117.  LeftEdge also cannot be a negative number.   The Leftedge of the NEXT
  118.  menu will equal the Leftedge + Width of the previous menu.
  119.  
  120.     Menu flags are provided to specify if the menu is currently enabled
  121.  or disabled (MENUENABLED),  or whether the Menus items are currently
  122.  displayed to the user.
  123.  
  124.     There is a pointer to the Menu name which will be displayed on the
  125.  top menu strip and a pointer to the FIRST MenuItem structure which
  126.  describes each ITEM the menu is to have.
  127.  
  128.                                                 
  129.  ITEMS STRUCTURE
  130.  ---------------
  131.  
  132.     Each menu usually has more than one ITEM.   An ITEM name is displayed
  133.  inside an ITEM BOX.   An ITEM BOX is the little box you see pop up if you
  134.  point and press the RIGHT mouse button at a specific menu name along the
  135.  top MENU STRIP.  MenuItem structures contain information that describes
  136.  the ITEM BOX and the item name.  
  137.  
  138.     Other information included in the MenuItem structure include a pointer
  139.  to the NEXT item in the menu.   Each item has its own set of flags that
  140.  can be used to tell Intuition to put a checkmark on the item.   You
  141.  can check the MenuItem whether it's checked by checking the CHECKED flag.
  142.  You also have to have the CHECKIT flag set.
  143.  
  144.     If your menu items are text names,  then you set the ITEMTEXT flag.
  145.  You can also specify an optional command key activation.   Setting
  146.  the COMMSEQ key, then putting the ascii value of the key into the
  147.  "Command" field, will cause that item to be selected when that key
  148.  and the RIGHT AMIGA key are pressed at the same time.
  149.  
  150.     If you want the item to be enabled and activated,  as most do,  then
  151.  set the ITEMENABLED flag.
  152.  
  153.     There are flags to describe how you want the highlighting to be done
  154.  when the item is selected.  HIGHCOMP will draw the selection box in the
  155.  Inverse mode,  while HIGHBOX will draw a box around the selection box.
  156.  I tried BOTH HIGHCOMP and HIGHBOX,  but nothing appeared to be selected.
  157.  
  158.     If you are using Images in your menu,  set the HIGHIMAGE flag.   You 
  159.  will usually want to set the HIGHBOX flag,  because it will stand out
  160.  better.   If you set the HIGHCOMP instead of the HIGHBOX,  the image
  161.  will be inverted;  it might not look right, especially if you are using
  162.  the Images to display color pattettes.
  163.  
  164.     An easy routine called NewMenu has been included which automatically
  165.  fills in the menuItem structure.   A description of it is listed below
  166.  in the "checklist" for setting up the menus.   This allows the use of
  167.  the Memory manager to allocate a lot of menu-item structures.
  168.  
  169.  
  170.  HANDLING MENU ACTIONS
  171.  ---------------------
  172.                                                 
  173.     Now that we have created the menus,  and have them popping out of the 
  174.  top of the screen,  we have to USE THEM.   Somehow,  we have to set up
  175.  our program so that certain actions can happen when the menu is activated
  176.  by the user.   This happens when the user lets go of the mouse button
  177.  after an item is selected.   Below is a "Checklist" which will enable you
  178.  to set up your program accurately.
  179.  
  180.  ____1.  In the NewWindow structure,  make sure the MENUPICK flag is specified.
  181.          If not,  then you will never get any Menu related events from Intuition.
  182.          The Intuition manual doesn't make this clear.
  183.  
  184.  ____2.  After setting the IDCMP flag MENUPICK in the NewWindow function,  you
  185.          have to make sure ALL the menu structures,  MenuItem structures,
  186.          and Intuitext or Image structures associated with MenuItem structures
  187.          are initialized properly.   After calling OpenWindow,  you must call
  188.          your Menu initialization procedure.   In this example,  I call it
  189.          NewMenu.   NewMenu essentually initializes the MenuItem,  structures
  190.          related to your menu.   It also fills in the pointer to the IntuiText
  191.          structure if the menu items are text,  or Image structure if the
  192.          menu contains ICONS or images.   You pass it the following:
  193.                                                 
  194.          o - Address of the Menu structure.  Ex:   &fmenu,  if the menu
  195.              structure is called fmenu.
  196.  
  197.          o - Address of an array of menu item name pointers.  These pointers
  198.              are put into the Intuitext items ONLY if the ITEMTEXT flag is set.
  199.              if not, then this pointer is a pointer to a bitmap image.
  200.  
  201.          o - Pointer to the first MenuItem structure in a linked list of other
  202.              structures.   This routine links them for you:  just tell
  203.              it where the FIRST MenuItem structure is,  and arrange for the
  204.              other structures to be right next to it.  Just declare an array
  205.              of MenuItem structures  (See the examples).   There are as many
  206.              MenuItem structures as there are Menu items.
  207.  
  208.          o - Pointer to the First intuitext structure in an array of them.  
  209.              There are as many IntuiText structures as there are MenuItem
  210.              structures.  If the ITEMTEXT flag is NOT set,  then we assume
  211.              that we are pointing to an Image structure.
  212.  
  213.          o - The Number of items this menu is to have.
  214.  
  215.          o - The Widths of each of the ITEM BOX WIDTHS.  Remember, the Item
  216.              box width is the pop up window width which contains the text item
  217.              names or the Images.
  218.  
  219.          o - MenuItem Flags.  All MenuItem structures will be filled with these
  220.              flags.   If you need different flags set,  you should do them
  221.              individually after calling this (these) functions.
  222.  
  223.  
  224.  ____3.  If any of the MenuItems need DIFFERENT flag settings.  Do them NOW.
  225.  
  226.  ____4.  Attach the menustrup to the window.   Call SetMenuStrip (See Example)
  227.          to tell Intuition to "Recognize" these new structures we just set up.
  228.  
  229.  ____5.  Now, modify the Event loop to "Recognize" menu actions by adding
  230.          a new "case" statement as shown below.   MENUPICK is the new case
  231.          we need to add.
  232.  
  233.     for (;;)
  234.     {
  235.   
  236.        if ((message = (struct IntuiMessage *)GetMsg(w->UserPort)) == 0L)  {
  237.            Wait(1L<<w->UserPort->mp_SigBit);    
  238.            continue;
  239.        }
  240.          class = message->Class;
  241.          code = message->Code;
  242.          ReplyMsg(message);
  243.          switch (class) {
  244.   
  245.             case CLOSEWINDOW : close_things();
  246.                                exit(0);
  247.                                break;
  248.   
  249.             case MENUPICK    : if (MENUNUM(code) != MENUNULL)
  250.                                  domenu(MENUNUM(code), ITEMNUM(code),
  251.                                         SUBNUM(code));
  252.                                break;
  253.   
  254.             case MOUSEBUTTONS: break;
  255.          }   /* Case */
  256.     }  /* for */
  257.  
  258.  ____6.  Now we define "domenu" as shown above.   We pass it three arguments,
  259.          and it is responsible for selecting the proper thing to do.   These
  260.          arguments are:
  261.      
  262.          o - Menu number....Specifies which menu.   Menu number 0 is the
  263.              left most menu on the screen.   The next one is menu number 1.
  264.  
  265.          o - Item number....Specifies the menu ITEM number from 0 to n.  
  266.  
  267.          o - Submenu item...Usually equals to 32 if there are none.
  268.  
  269.          See the example program included in this tutorial.   Normally,
  270.          your code might look like the example, but it usually depends
  271.          on your personal taste.  I used the "case" example because it
  272.          is easy to read.   A true C jock might hammer it into a more
  273.          efficient form.
  274.  
  275.  REQUESTERS - The easy kind
  276.  --------------------------
  277.  
  278.     Requesters turned out to be a real pain in the ....   The Intuition
  279.  manual really lacks good documentation,  and because of the kindness of
  280.  Jim, and Dave Lucas at Amiga R&D,  I was able to sucessfully create a
  281.  custom requester.                              
  282.  
  283.     Even simple AutoRequest turned out to be a pain because of the way
  284.  the arguments are passed to AutoRequest.   Because the Lattice C thinks
  285.  of "int's" as 32 bits,  and Manx C uses "int's" as 16 bits,   It wasn't
  286.  possible to pass the arguments properly without the "L" after the literal
  287.  digit.   After much fussing around with the borders,  and gadget rectangles
  288.  I was able to make a few sample AutoRequests.   The thing you have to
  289.  remember when using AutoRequests- If you're using a C compiler using
  290.  16 bit ints- is that the size and position arguments are 32 bits.
  291.  
  292.    val = AutoRequest(w, &AutoText, &TRUEtext, &FALSEtext, 0L, 0L, 319L, 60L );
  293.    if (val)
  294.         printf("TRUE\n");
  295.     else
  296.         printf("FALSE\n");
  297.     
  298.  
  299.     Above is a sample of how to pass numeric arguments to AutoRequest.
  300.  Note the "L" after the digits.  AutoText, TRUEtext, and FALSEtext are
  301.  IntuiText structures.   If you are using Lattice C,  remove the "L's"
  302.  shown above.
  303.                                                 
  304.     This function essentually "steals" control from your main "Message
  305.  loop",  and handles messages ONLY generated by the requester.   You
  306.  COULD use the main program event loop and add an extra case statement
  307.  switching on REQCLEAR messages.
  308.  
  309.  
  310.  
  311.  BUILDING YOUR OWN REQUESTERS
  312.  ----------------------------
  313.  
  314.     In order to build your own requester,   you will need to declare one or
  315.  more "Requester" structures:  at least ONE gadget AND border per structure.   
  316.  Naturally, you will probably want your own custom Image or Text describing 
  317.  to the user what the requester is to do.
  318.  
  319.  Example:
  320.  struct Requester req;     /* Custom Requester structure */
  321.  
  322.     After all the structures are declared,   you will have to call
  323.  InitRequester, passing it a pointer to your "Requester" structure.
  324.  NOTE:  In the Intuition manual,  the example showing the declaration
  325.  and initialization of the Requester structure really isn't necessary.
  326.  InitRequester will zap all of those fields anyway.   After talking to
  327.  Amiga folks,  they admitted that it really isn't appropriate to 
  328.  pre-initialize the Requester structure.
  329.  
  330.  Example:
  331.     InitRequester(&req);          /* Init the requestor              */
  332.  
  333.  
  334.     You DON'T HAVE TO Initialize the "Requester" structure,  because
  335.  Intuition does it FOR you.   InitRequester will ZERO OUT all the fields
  336.  in the Requester structure,  and also do other things.
  337.  
  338.     AFTER calling InitRequester,  you then have to initialize the specific
  339.  fields in the Requester structure as shown below.   This is usually done
  340.  AFTER creating the window, and BEFORE entering the event loop.
  341.  
  342.     req.LeftEdge  = 20;
  343.     req.TopEdge   = 20;
  344.     req.Width     = 250;
  345.     req.Height    = 80;
  346.     req.ReqGadget = &ongad;        /* First gadget in Requester */
  347.     req.ReqText   = &text;         /* Text for requester */
  348.     req.BackFill  = 1;             /* BackGnd color to window */
  349.     req.Flags     = 0;
  350.     req.ReqBorder = &out_border;  /* OutSide bord - Must have at least one */
  351.   
  352.     &ongad = Pointer to the first gadget in the requester.
  353.  
  354.     &text  = Pointer to Intuitext text structure and renders text into
  355.              the requester.
  356.  
  357.     &out_border = This is the outside border to the structure.   You MUST 
  358.              have at least ONE border structure.
  359.  
  360.  
  361.  USING CUSTOM REQUESTERS
  362.  -----------------------
  363.  
  364.     This was the part where I had the most problems.   I had to get help
  365.  from Amiga to figure this out.   
  366.  
  367.     Lets suppose the programmer wants to bring up a requester when a menu
  368.  choice is made.   According to the Intuition manual,   you call
  369.  Request()  function,  and the requester is supposed to come up on the
  370.  sceen.   It does.   Just fine.    HOWEVER-  what then??!?  
  371.  
  372.     You are then supposed to go into your own "wait" loop,  getting
  373.  messages from the IDCMP.   You are waiting for Intuition to send you
  374.  a REQCLEAR message class.   This means your NewWindow structure BETTER
  375.  have the REQCLEAR flag set,  otherwise you have no way to tell when
  376.  the user presses the gadget in your request.
  377.  
  378.     After you get the REQCLEAR message,  you will need to snatch a copy
  379.  if the IAddress field of the IntuiMessage structure BEFORE you reply
  380.  to the message.    The IAddress field contains the Address of the
  381.  gadget structure acted upon by the user.   You can use the GadgetID
  382.  field as your own personal method of identifying the gadget and act on it
  383.  accordingly.
  384.  
  385.     When you have finished processing this action,   you might think that
  386.  the appropriate thing to do would be to call EndGadget.   But as you
  387.  can see,  I DON'T.  I set the ENDGADGET flag in the Activation
  388.  field of the gadget.  If this flag is set,  you DON'T have to call
  389.  EndGadget; and if you do,  your system goes away.   Intuition closes
  390.  the Requester for you when THAT gadget is selected.    You can have
  391.  MORE THAN ONE gadget with the ENDGADGET set.   If you DON'T set the
  392.  ENDGADGET,  then it's safe to call "EndRequest".   A good way to tell
  393.  when Intuition trashes your requester is to examine the FirstRequest
  394.  field in the Window record.   The "Request" function stuffs the
  395.  FirstRequest field with a pointer to the Requester structure.   EndRequest
  396.  removes this pointer and sets it to NULL.    It might be safe to
  397.  use this procedure while calling EndRequest.
  398.  
  399.      if (w->FirstRequest != NULL)
  400.          EndRequest (&req, w);
  401.  
  402.  
  403.  PROCEDURE FOR MAKING AND USING CUSTOM REQUESTERS
  404.  ------------------------------------------------
  405.  
  406.  o - Sketch out the requester in rough draft form.   Decide how many
  407.      gadgets the requester is to have.
  408.  
  409.  o - Declare and initialize all the Gadget,  Intuitext,   image or
  410.      other structures needed by this requester.   Remember to set the
  411.      REQGADGET flag in the GadgetType fields of ALL the gadgets.   Also,
  412.      set the ENDGADGET flag in the gadget's Activation field if you want
  413.      the gadget to automatically go away after processing the REQCLEAR
  414.      message.   This can take several pages of structure definitions.
  415.      
  416.      If your Requester Gadget uses an Image instead of text,  you have
  417.      to set BOTH  GADGIMAGE | GADGHIMAGE flags in the "Flags" field
  418.      and then leave pointers to their "Image" structure like the
  419.      following example:
  420.  
  421.  o - If you want to process a gadget within a requester,  but leave the
  422.      requester up on the screen,  that gadget MUST have the 
  423.      GADGIMMEDIATE flag set as shown below.   This causes Intuition to
  424.      send you a GADGETDOWN message class.   Remember to SET the 
  425.      GADGETDOWN flag in the NewWindow structure,  otherwise you WON'T
  426.      get the Message back from Intuition.
  427.      
  428.      
  429.  struct Gadget face_gad = {
  430.    NULL,
  431.    30, 18,                              /* LeftEdge, TopEdge     */
  432.    32, 20,                              /* Width,  Height        */
  433.    GADGHIMAGE | GADGIMAGE | GADGHIMAGE, /* Flag                  */
  434.    RELVERIFY | GADGIMMEDIATE,           /* Activation            */
  435.    BOOLGADGET | REQGADGET,              /* GadgetType            */
  436.    (APTR)&smil_face,                    /* GadgetRender - Border */
  437.    (APTR)&tong_face,                    /* SelectRender          */
  438.    &gag_me,                             /* "Gag me"              */
  439.    NL, NL, NL, NL                       /* Mut Excl, Spec Info,  */
  440.  };
  441.  
  442.      The &smil_face is a pointer to an "Image" structure.  See listing
  443.      included in this tutorial.
  444.  
  445.  o - If you are building your own custom requesters,  you will need to
  446.      set the IDCMP flag REQCLEAR in your NewWindow structure as long as
  447.      you expect Intuition to clear your requester for you by setting the
  448.      ENDGADGET flag in the gadget's Activation field,  and expect to 
  449.      get the REQCLEAR message from IDCMP.   If you DON'T set this flag, 
  450.      then YOU must call "EndRequest" to remove your requester.
  451.  
  452.  o - In your initialization code somewhere before your message loop,   you
  453.      will need to call InitRequester.   Pass it the pointer to your
  454.      requester structure.   This clears out all the fields of your
  455.      requester and does other internal initializations.   Example:
  456.  
  457.     InitRequester(&req);          /* Init the requestor              */
  458.  
  459.      You need to do this for Every requester you expect to have.
  460.  
  461.  o - Initialize the fields in your requester array.   It should always
  462.      be done AFTER calling InitRequester because it zeroes out all the
  463.      elements in the structure.   Example:
  464.  
  465.  /* Init the fields in the Requester structure */
  466.   
  467.     req.LeftEdge  = 20;
  468.     req.TopEdge   = 20;
  469.     req.Width     = 250;
  470.     req.Height    = 80;
  471.     req.ReqGadget = &ongad;        /* First gadget */
  472.     req.ReqText   = &text;         /* Text for requester */
  473.     req.BackFill  = 1;             /* BackGnd color to window */
  474.     req.Flags     = 0;
  475.     req.ReqBorder = &out_border;  /* Must have at least one */
  476.  
  477.   
  478.  CALLING UP THE REQUESTER
  479.  ------------------------                       
  480.  
  481.  o - Call "Request", pass it a pointer to your request structure and a
  482.      pointer to your window.   This brings up the requester box in your
  483.      window.   The code listed below checks the returned value.  If
  484.      the value is 1,  then it was sucessful.  "do_req(&req)" does the next
  485.      thing explained below.
  486.      
  487.  
  488.              case   TEX_REQS: if ((val = Request(&req, w)) == 1) {
  489.                                 do_req(&req);
  490.                               }
  491.                               break;
  492.  
  493.  
  494.  o - Go into a Wait and GetMsg loop "listening" for the REQCLEAR message
  495.      class.   Before "Replying" to the message,  store the IAddress field
  496.      and the class of the IntuiMessage into local variables,  then Reply
  497.      to the Message.   If the "class" is equal to REQCLEAR,  then use the
  498.      IAddress value (which points to the gadget that user pressed) to
  499.      identify the gadget, and do the appropriate action.    You DON'T
  500.      have to call EndRequest after handling gadget action IF you
  501.      set the ENDGADGET flag in the gadget structure that was explained
  502.      above.  If you have to process any gadgets,  you have to check for
  503.      GADGETDOWN message class and process it.    The mes->IAddress
  504.      value below contains a pointer to the Gadget structure chosen
  505.      by the user.    I use the GadgID field in the gadget structure
  506.      to identify the gadget.    I don't necessarily need to know which
  507.      requester had the gadget.   I could find out by looking at the
  508.      FirstGadget field in the Window record,  then traversing through
  509.      the linked list to the LAST requester in the list.
  510.  
  511.  
  512.  do_req(request)
  513.  struct Requester *request;          /* Which requester to process */
  514.  {
  515.        int looping = TRUE;
  516.        struct IntuiMessage *mes;
  517.        struct Gadget *gad;           /* Gadget chosen */
  518.        ULONG class;
  519.   
  520.        while (looping)
  521.        {
  522.            if ((mes = (struct IntuiMessage *)GetMsg(w->UserPort)) == 0L) {
  523.               Wait(1L<<w->UserPort->mp_SigBit); 
  524.               continue;     /** Be nice to the other programs **/
  525.            }
  526.               class = mes->Class;
  527.               gad = (struct Gadget *)mes->IAddress;
  528.               ReplyMsg(mes);
  529.               if (class == REQCLEAR) {
  530.                  looping = FALSE;          /* We exit and Intuition will */
  531.               }                            /* remove the requester  */
  532.               if (class == GADGETDOWN) {
  533.   
  534.                  switch (gad->GadgetID) {
  535.   
  536.                     case TRUE_BUTT:  printf("true button ...\n");
  537.                                      break;
  538.                     case FALSE_BUTT: printf("false button...\n");
  539.                                      break;
  540.                     case FACE:       printf("Ouch!!..That hurt!!\n");
  541.                                      break;
  542.                  }
  543.               }
  544.        }
  545.  }                                              
  546.